<?php
define('EZSQL_VERSION', 'WP1.25');

/**
 * @since 0.71
 */
define('OBJECT', 'OBJECT', true);

/**
 * @since {@internal Version Unknown}}
 */
define('OBJECT_K', 'OBJECT_K', false);

/**
 * @since 0.71
 */
define('ARRAY_A', 'ARRAY_A', false);

/**
 * @since 0.71
 */
define('ARRAY_N', 'ARRAY_N', false);

class NDatabase{
  private static $_instances;
  var $_debug=false;
  var $_table_prefix="neo_";
  var $_nameQuote="`";
	
function __construct($options){
	//print_r($options);

	$this->_resource = &mysql_connect($options['host'],$options['user'],$options['password']);
	if (!$this->_resource) {NError::raiseError('invalid connection','invalid mysql connection'); }
	//	print_r($options);
	if ($options['prefix']) $this->_table_prefix=$options['prefix'];
	if ($options['database'])
	 $this->select_db($options['database']);
	$this->setQuery("SET NAMES utf8");
        $this->query();
        $this->setCachePrefix("sql");
	


// FROM DKLAB
	if (@fopen(FRAMEWORK_DIR.'/libraries/cache/Lite.php', 'r', true)) {
            $tmp_dirs = array(
                ini_get('session.save_path'),
                getenv("TEMP"),
                getenv("TMP"),
                getenv("TMPDIR"),
                '/tmp'
            );

		if ($options['cache_dir']){
		   array_unshift( $tmp_dirs,$options['cache_dir']);
		}
	
		
            foreach ($tmp_dirs as $dir) {
                if (!$dir) continue;
                $fp = @fopen($testFile = $dir . '/DbSimple_' . md5(getmypid() . microtime()), 'w');
                if ($fp) {
                    fclose($fp);
                    unlink($testFile);                
                    require_once FRAMEWORK_DIR.'/libraries/cache/Hashed_Cache_Lite.php'; 
	     //require_once FRAMEWORK_DIR.'/libraries/cache/Lite.php'; 
               
                    $t =& new Hashed_Cache_Lite(array('cacheDir' => $dir.'/', 'lifeTime' => null, 'automaticSerialization' => true,'fileNameProtection'=>false));
                    $this->_cacher =& $t;
                    break;
                }

            }
        }
	
	
}

function Close(){
	mysql_close($this-_resource);
}
static function getInstance($options){
	
$key =  serialize($options);
if (!isset(self::$_instances[$key])) {
      $c = __CLASS__;
      self::$_instances[$key] = new $c($options);
    }
    return self::$_instances[$key];
  }
function get_insert_id(){
	return mysql_insert_id($this->_resource);
}

function insertObject( $table, &$object, $keyName = NULL )
	{
		if (is_array($object)) $object = (object) $object;
		$fmtsql = "INSERT INTO $table ( %s ) VALUES ( %s ) ";
		$fields = array();
		foreach (get_object_vars( $object ) as $k => $v) {
			if (is_array($v) or is_object($v) or $v === NULL) {
				continue;
			}
			if ($k[0] == '_') { // internal field
				continue;
			}
			$fields[] = $this->nameQuote( $k );
			$values[] = $this->isQuoted( $k ) ? $this->Quote( $v ) : (int) $v;
		}
		$this->setQuery( sprintf( $fmtsql, implode( ",", $fields ) ,  implode( ",", $values ) ) );
		if (!$this->query()) {
			return false;
		}
		$id = $this->get_insert_id();
		if ($keyName && $id) {
			$object->$keyName = $id;
		}
		return true;
	}
function replaceObject( $table, &$object, $keyName = NULL )
	{
		if (is_array($object)) $object = (object) $object;
		$fmtsql = "REPLACE INTO $table ( %s ) VALUES ( %s ) ";
		$fields = array();
		foreach (get_object_vars( $object ) as $k => $v) {
			if (is_array($v) or is_object($v) or $v === NULL) {
				continue;
			}
			if ($k[0] == '_') { // internal field
				continue;
			}
			$fields[] = $this->nameQuote( $k );
			$values[] = $this->isQuoted( $k ) ? $this->Quote( $v ) : (int) $v;
		}
		$this->setQuery( sprintf( $fmtsql, implode( ",", $fields ) ,  implode( ",", $values ) ) );
		if (!$this->query()) {
			return false;
		}
		$id = $this->get_insert_id();
		if ($keyName && $id) {
			$object->$keyName = $id;
		}
		return true;
	}

function updateObject( $table, &$object, $keyName, $updateNulls=true )
	{
		$fmtsql = "UPDATE $table SET %s WHERE %s";
		$tmp = array();
		foreach (get_object_vars( $object ) as $k => $v)
		{
			if( is_array($v) or is_object($v) or $k[0] == '_' ) { // internal or NA field
				continue;
			}
			if( $k == $keyName ) { // PK not to be updated
				$where = $keyName . '=' . $this->Quote( $v );
				continue;
			}
			if ($v === null)
			{
				if ($updateNulls) {
					$val = 'NULL';
				} else {
					continue;
				}
			} else {
				$val = $this->isQuoted( $k ) ? $this->Quote( $v ) : (int) $v;
			}
			$tmp[] = $this->nameQuote( $k ) . '=' . $val;
		}
		$this->setQuery( sprintf( $fmtsql, implode( ",", $tmp ) , $where ) );
		return $this->query();
	}
function getErrorNum(){
	mysql_errno($this->_resource);

}

function getErrorMsg(){
	mysql_error($this->_resource);

}
function select_db($db_name){
	mysql_select_db($db_name,$this->_resource);
}

function getEscaped( $text, $extra = false )
	{
		$result = mysql_real_escape_string( $text, $this->_resource );
		if ($extra) {
			$result = addcslashes( $result, '%_' );
		}
		return $result;
	}

	function nameQuote( $s )
	{
		$q = $this->_nameQuote;
		if (strlen( $q ) == 1) {
			return $q . $s . $q;
		} else {
			return $q{0} . $s . $q{1};
		}
	}

	function isQuoted( $fieldName )
	{
		if (isset($this->_hasQuoted) && $this->_hasQuoted) {
			return in_array( $fieldName, $this->_quoted );
		} else {
			return true;
		}
	}

	function Quote( $text, $escaped = true )
	{
		return '\''.($escaped ? $this->getEscaped( $text ) : $text).'\'';
	}

	function setQuery( $sql, $offset = 0, $limit = 0, $prefix='#__' )
	{
		$this->_sql		= $this->replacePrefix( $sql, $prefix );
		$this->_limit	= (int) $limit;
		$this->_offset	= (int) $offset;
		if ($this->_limit > 0 || $this->_offset > 0) {
			$this->_sql .= ' LIMIT '.$this->_offset.', '.$this->_limit;
		}
	}

function replacePrefix( $sql, $prefix='#__' )
	{
		$sql = trim( $sql );

		$escaped = false;
		$quoteChar = '';

		$n = strlen( $sql );

		$startPos = 0;
		$literal = '';
		while ($startPos < $n) {
			$ip = strpos($sql, $prefix, $startPos);
			if ($ip === false) {
				break;
			}

			$j = strpos( $sql, "'", $startPos );
			$k = strpos( $sql, '"', $startPos );
			if (($k !== FALSE) && (($k < $j) || ($j === FALSE))) {
				$quoteChar	= '"';
				$j			= $k;
			} else {
				$quoteChar	= "'";
			}

			if ($j === false) {
				$j = $n;
			}

			$literal .= str_replace( $prefix, $this->_table_prefix,substr( $sql, $startPos, $j - $startPos ) );
			$startPos = $j;

			$j = $startPos + 1;

			if ($j >= $n) {
				break;
			}

			// quote comes first, find end of quote
			while (TRUE) {
				$k = strpos( $sql, $quoteChar, $j );
				$escaped = false;
				if ($k === false) {
					break;
				}
				$l = $k - 1;
				while ($l >= 0 && $sql{$l} == '\\') {
					$l--;
					$escaped = !$escaped;
				}
				if ($escaped) {
					$j	= $k+1;
					continue;
				}
				break;
			}
			if ($k === FALSE) {
				// error in the query - no end quote; ignore it
				break;
			}
			$literal .= substr( $sql, $startPos, $k - $startPos + 1 );
			$startPos = $k+1;
		}
		if ($startPos < $n) {
			$literal .= substr( $sql, $startPos, $n - $startPos );
		}
		return $literal;
	}
	function getQuery()
	{
		return $this->_sql;
	}

function query()
	{
		if (!is_resource($this->_resource)) {
			return false;
		}
 

		
		if ($this->_debug) {
		//	_DEBUG($this->_sql);
			$this->_ticker++;
			
			$oLogger = NFactory::getLogger_old();
		$oLogger->startTime($this->_ticker);
		}
		$this->_errorNum = 0;
		$this->_errorMsg = '';
		
		$this->_cursor = mysql_query( $this->_sql, $this->_resource );

		if (!$this->_cursor)
		{
			$this->_errorNum = mysql_errno( $this->_resource );
			$this->_errorMsg = mysql_error( $this->_resource )." SQL=$this->_sql";

			if ($this->_debug) {
				_DEBUG('NDatabaseMySQL::query: '.$this->_errorNum.' - '.$this->_errorMsg);
				
				NError::raiseError(500, 'NDatabaseMySQL::query: '.$this->_errorNum.' - '.$this->_errorMsg );
			}
			return false;
		}
		if ($this->_debug) {
		//	_DEBUG($this->_sql);
		//	$this->_ticker++;
		//	$this->_log[$this->_ticker] = $this->_sql;
	$oLogger->stopTime($this->_ticker);
	$this->_log[$this->_ticker] = $this->_sql." " .($oLogger->logend[$this->_ticker]- $oLogger->logstart[$this->_ticker]);
	_DEBUG($this->_sql." " .($oLogger->logend[$this->_ticker]- $oLogger->logstart[$this->_ticker]));
		}

		return $this->_cursor;
	}
	function getLastError(){
	return (object) array('num'=>$this->_errorNum,'msg'=>$this->_errorMsg);
	}
	/**
	* This global function loads the first row of a query into an object
	*
	* @access	public
	* @return 	object
	*/
	function &loadObject( )
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$ret = null;
		if ($object = mysql_fetch_object( $cur )) {
			$ret = $object;
		}
		mysql_free_result( $cur );
		return $ret;
	}
	/**
	* Load current record by  cur connection
	*
	* @access	public
	* @return 	object
	*/
	function loadObjectIterate($cur=null)
	{
		
		if (!$cur) {
			if (!($cur = $this->query())) {
				return null;
			}
		}
		$ret = null;
		if ($object = mysql_fetch_object( $cur )) {
			$ret = $object;
		}
		if (!$ret)
		mysql_free_result( $cur );
		return $ret;
	}

	/**
	* Load a list of database objects
	*
	* If <var>key</var> is not empty then the returned array is indexed by the value
	* the database key.  Returns <var>null</var> if the query fails.
	*
	* @access	public
	* @param string The field name of a primary key
	* @return array If <var>key</var> is empty as sequential list of returned records.
	*/
	function loadObjectList( $key='' )
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$array = array();
		while ($row = mysql_fetch_object( $cur )) {
			if ($key) {
				$array[$row->$key] = $row;
			} else {
				$array[] = $row;
			}
		}
		mysql_free_result( $cur );
		return $array;
	}
	function loadTable(&$table,$key_field=''){
	if ($key_field) 
	$tbl_key=$key_field;
	else 
	$tbl_key=$table->_tbl_key;

	$sql="SELECT * FROM ".$table->_tbl.
	" WHERE ".$tbl_key."='".$table->$tbl_key."' LIMIT 0,1";
	$this->setQuery($sql);
	$this->query($sql);
	$obj = $this->loadObject();
	$table->bind($obj);
	
	}
	
	function deleteTable(&$table){
	$tbl_key=$table->_tbl_key;
	$sql="DELETE  FROM ".$table->_tbl." WHERE ".$tbl_key."=".$table->$tbl_key;
	$this->setQuery($sql);
	$this->query($sql);
	}
	/**
	* TODO testing
	*/
	function deleteObject($table,&$object,$keyName){
	
	if (!is_array($keyName)) $keyName=array($keyName);		
	foreach ($keyName as $key) {
			$aConditions[]=$key."='".$object->$key."'";
		}
	
	$_sql = "DELETE  FROM ".$table." WHERE 1 and ( ".join(" and ",$aConditions).")";
	$this->setQuery($_sql);
	//echo $this->replacePrefix($_sql);
	$_ret=$this->query($_sql);
	unset($object);
	return $_ret;
	
		
	}

	/**
	* Fetch a result row as an associative array
	*
	* @access	public
	* @return array
	*/
	function loadAssoc()
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$ret = null;
		if ($array = mysql_fetch_assoc( $cur )) {
			$ret = $array;
		}
		mysql_free_result( $cur );
		return $ret;
	}
	/**
	 * This method loads the first field of the first row returned by the query.
	 *
	 * @access	public
	 * @return The value returned in the query or null if the query failed.
	 */
	function loadResult()
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$ret = null;
		if ($row = mysql_fetch_row( $cur )) {
			$ret = $row[0];
		}
		mysql_free_result( $cur );
		return $ret;
	}

/**
	 * ADODB compatability function
	 *
	 * @access	public
	 * @param	string SQL
	 * @return	object

	 */
	function Execute( $query )
	{
		require_once(FRAMEWORK_DIR."/libraries/neocasta/database/recordset.php");

		$query = trim( $query );
		$this->setQuery( $query );
		if (eregi( '^select', $query )) {
			$result = $this->loadRowList();
			return new NRecordSet( $result );
		} else {
			$result = $this->query();
			if ($result === false) {
				return false;
			} else {
				return new NRecordSet( array() );
			}
		}
	}
	/**
	* Load a list of database rows (numeric column indexing)
	*
	* @access public
	* @param string The field name of a primary key
	* @return array If <var>key</var> is empty as sequential list of returned records.
	* If <var>key</var> is not empty then the returned array is indexed by the value
	* the database key.  Returns <var>null</var> if the query fails.
	*/
	function loadRowList( $key=null )
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$array = array();
		while ($row = mysql_fetch_row( $cur )) {
			if ($key !== null) {
				$array[$row[$key]] = $row;
			} else {
				$array[] = $row;
			}
		}
		mysql_free_result( $cur );
		return $array;
	}

	function loadAssocList( $key=null )
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$array = array();
		while ($row = mysql_fetch_assoc( $cur )) {
			if ($key !== null) {
				$array[$row[$key]] = $row;
			} else {
				$array[] = $row;
			}
		}
		mysql_free_result( $cur );
		return $array;
	}
	function loadArrayList( $key=null )
	{
		if (!($cur = $this->query())) {
			return null;
		}
		$array = array();
		while ($row = mysql_fetch_array( $cur )) {
			if ($key !== null) {
				$array[$row[$key]] = $row;
			} else {
				$array[] = $row;
			}
		}
		mysql_free_result( $cur );
		return $array;
	}
/**
* WP style 
*/
	/**
	 * Kill cached query results.
	 *
	 * @since 0.71
	 */
	function flush() {
		$this->last_result = array();
		$this->col_info = null;
		$this->last_query = null;
	}
	/**
	 * Insert an array of data into a table.
	 *
	 * @since 2.5.0
	 *
	 * @param string $table WARNING: not sanitized!
	 * @param array $data Should not already be SQL-escaped
	 * @return mixed Results of $this->query()
	 */
	function WPinsert($table, $data) {
		$data = NWPFunctions::add_magic_quotes($data);
		$fields = array_keys($data);
		return $this->WPquery("INSERT INTO $table (`" . implode('`,`',$fields) . "`) VALUES ('".implode("','",$data)."')");
	}
	/**
	 * Perform a MySQL database query, using current database connection.
	 *
	 * More information can be found on the codex page.
	 *
	 * @since 0.71
	 *
	 * @param string $query
	 * @return unknown
	 */
	function WPquery($query) {
		
		$return_val = 0;
		$this->flush();

		// Log how the function was called
		$this->func_call = "\$db->query(\"$query\")";

		// Keep track of the last query for debug..
		$this->last_query = $query;

		$this->setQuery($query);
		$cur = $this->query();
		++$this->num_queries;


		

		if ( preg_match("/^\\s*(insert|delete|update|replace) /i",$query) ) {
			$this->rows_affected = mysql_affected_rows($this->_resource);
			// Take note of the insert_id
			if ( preg_match("/^\\s*(insert|replace) /i",$query) ) {
				$this->insert_id = mysql_insert_id($this->_resource);
			}
			// Return number of rows affected
			$return_val = $this->rows_affected;
		} else {
			$i = 0;
			while ($i < @mysql_num_fields($this->_cursor)) {
				$this->col_info[$i] = @mysql_fetch_field($this->_cursor);
				$i++;
			}
			$num_rows = 0;
			while ( $row = @mysql_fetch_object($this->_cursor) ) {
				$this->last_result[$num_rows] = $row;
				$num_rows++;
			}

			@mysql_free_result($this->_cursor);

			// Log number of rows the query returned
			$this->num_rows = $num_rows;

			// Return number of rows selected
			$return_val = $this->num_rows;
		}

		return $return_val;
	}

	/**
	 * Retrieve one variable from the database.
	 *
	 * This combines the functionality of wpdb::get_row() and wpdb::get_col(),
	 * so both the column and row can be picked.
	 *
	 * It is possible to use this function without executing more queries. If
	 * you already made a query, you can set the $query to 'null' value and just
	 * retrieve either the column and row of the last query result.
	 *
	 * @since 0.71
	 *
	 * @param string $query Can be null as well, for caching
	 * @param int $x Column num to return
	 * @param int $y Row num to return
	 * @return mixed Database query results
	 */
	function get_var($query=null, $x = 0, $y = 0) {
		$this->func_call = "\$db->get_var(\"$query\",$x,$y)";
		if ( $query ){
			$this->WPQuery($query);
			
		}

		// Extract var out of cached results based x,y vals
		if ( !empty( $this->last_result[$y] ) ) {
			$values = array_values(get_object_vars($this->last_result[$y]));
		}

		// If there is a value return it else return null
		return (isset($values[$x]) && $values[$x]!=='') ? $values[$x] : null;
	}
	/**
	 * Retrieve one column from the database.
	 *
	 * @since 0.71
	 *
	 * @param string $query Can be null as well, for caching
	 * @param int $x Col num to return. Starts from 0.
	 * @return array Column results
	 */
	function get_col($query = null , $x = 0) {
		if ( $query )
			$this->WPquery($query);

		$new_array = array();
		// Extract the column values
		for ( $i=0; $i < count($this->last_result); $i++ ) {
			$new_array[$i] = $this->get_var(null, $x, $i);
		}
		return $new_array;
	}
	/**
	 * Retrieve one row from the database.
	 *
	 * @since 0.71
	 *
	 * @param string $query SQL query
	 * @param string $output ARRAY_A | ARRAY_N | OBJECT
	 * @param int $y Row num to return
	 * @return mixed Database query results
	 */
	function get_row($query = null, $output = OBJECT, $y = 0) {
		$this->func_call = "\$db->get_row(\"$query\",$output,$y)";
		if ( $query )
			$this->WPquery($query);
		else
			return null;

		if ( !isset($this->last_result[$y]) )
			return null;

		if ( $output == OBJECT ) {
			return $this->last_result[$y] ? $this->last_result[$y] : null;
		} elseif ( $output == ARRAY_A ) {
			return $this->last_result[$y] ? get_object_vars($this->last_result[$y]) : null;
		} elseif ( $output == ARRAY_N ) {
			return $this->last_result[$y] ? array_values(get_object_vars($this->last_result[$y])) : null;
		} else {
			/** TODO сделать через NError*/
			die( "Output type must be one of: OBJECT, ARRAY_A, ARRAY_N");
			//$this->print_error(/*WP_I18N_DB_GETROW_ERROR*/" \$db->get_row(string query, output type, int offset) -- Output type must be one of: OBJECT, ARRAY_A, ARRAY_N"/*/WP_I18N_DB_GETROW_ERROR*/);
		}
	}
	
	
	function get_caller() {
		// requires PHP 4.3+
		if ( !is_callable('debug_backtrace') )
			return '';

		$bt = debug_backtrace();
		$caller = '';

		foreach ( $bt as $trace ) {
			if ( @$trace['class'] == __CLASS__ )
				continue;
			elseif ( strtolower(@$trace['function']) == 'call_user_func_array' )
				continue;
			elseif ( strtolower(@$trace['function']) == 'apply_filters' )
				continue;
			elseif ( strtolower(@$trace['function']) == 'do_action' )
				continue;

			$caller = $trace['function'];
			break;
		}
		return $caller;
	}

/**
* DKLab
*/
    /**
     * mixed _cache($hash, $result=null)
     * Calls cache mechanism if possible.
     */
    function _cache($hash, $result=null)
    {
	
        if (is_callable($this->_cacher)) {
            return call_user_func($this->_cacher, $hash, $result);
        } else if (is_object($this->_cacher) && method_exists($this->_cacher, 'get') && method_exists($this->_cacher, 'save')) {
            if (null === $result)
                return $this->_cacher->get($hash);
            else
                $this->_cacher->save($result, $hash);
        }
        else return false;
    }
   function getSqlHash(){
	$_sql = $this->_sql;
	
	//echo $_sql;
	$_hash =  $this->_cachePrefix . md5(serialize($_sql));
	//echo $_hash;
	return($_hash);
   }
    /**
     * string setIdentPrefix($prx)
     * Set cache prefix used in key caclulation.
     */
    function setCachePrefix($prx)
    {
        $old = $this->_cachePrefix;
        if ($prx !== null) $this->_cachePrefix = $prx;
        return $old;
    }
    function affected_rows(){
	return mysql_affected_rows($this->_resource); 
	}
}



?>